home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
prog
/
asm_n_z.arj
/
SCRNSAV2.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-09-01
|
27KB
|
735 lines
page ,132
title scrnsav2 : Screen Saver for PS/2
;
; Copyright (c) 1988 Alan Ballard
;
; This program is a TSR (Terminate and Stay Resident) utility
; "screen saver". It turns off the video after a specified period
; of inactivity (no keyboard or mouse action). Touching the keyboard or
; the mouse will reactivate the screen.
;
; A VGA monitor and the PS/2 extended bios are required.
;
; The program stays resident only the first time it is run. It may be reexecuted
; to disable blanking or to change the interval.
;
; Options are:
; integer - blanking time in minutes (default 5 minutes)
; \d - disable blanking
; \e - enable blanking (default)
; \m integer - set the "multiplex number" to use for communicating
; with the loaded version.
;
; Interrupt numbers
Keyboard@ equ 09h
Video_Io@ equ 10h
System_Services@ equ 15h
Timer@ equ 1ch
Dos@ equ 21h
Multiplex@ equ 2fh
Mouse@ equ 74h
; Video I/O function numbers
Read_Display_Combination_Code equ 1a00h
; System services function numbers
Keyboard_Intercept equ 4fh
; Multiplex function numbers
Get_Installed_State equ 0
; Scrnsav2 multiplex functions
Scrnsav_Multiplex_Number equ 150 ; random choice
Scrnsav_Disable equ 10h
Scrnsav_Enable equ 11h
Scrnsav_Set_Interval equ 12h
; DOS function numbers
Print_String equ 09h
Print_Char equ 02h
Set_Int equ 25h
Get_Int equ 35h
TSR equ 31h
Terminate equ 4ch
; Miscellaneous constants
Ticks_Per_Minute equ 1092 ; 60*18.2
Default_Time equ 5 ; five minute default
Max_Time equ 30 ; 30 minute maximum (30*1092=32760)
Cr equ 0dh
Lf equ 0ah
True equ 0ffh
False equ 00h
; VGA adaptor constants
SAR equ 03c4h ; Sequencer Address Register
SDR equ 03c5h ; Sequencer Data Register
Clocking_Mode_Index equ 01 ; SAR index for clocking mode reg
Screen_Mask equ 20h ; Mask for screen off bit.
Scrnsav2 segment para 'code'
assume cs:Scrnsav2,ds:nothing,es:nothing
;
; Overlay permanent data on PSP.
;
org 05ch ; not supposed to change anything before this
Old_Mouse dd ? ; save old int 74 (mouse)
Old_Timer dd ? ; save old timer tick
Old_Ss dd ? ; save old system services
Old_Multiplex dd ? ; save old multiplex
;
Idle_Count dw ? ; Ticks till we shutdown
Idle_Max dw ? ; Initial ticks
Save_Reg db ? ; Place to save CRT register value
Multiplex_No db ? ; Multiplex number we're using
Disabled db ? ; enable/disable flag
;
; Define command line in PSP...
;
org 080h
Cmdline label byte
page
; Code starts at offset 0100h for COM file.
org 0100h ; beginning for .com programs
Start: jmp Initialize ; initialization code is at end.
;
; int 15 (system services) enters here. Checks for keyboard intercept
; which shows some activity. Passed on to old interrupt routine in all
; cases.
;
System_Services proc far
cmp ah,Keyboard_Intercept
jne NotKey
call Action ; record something happenned
NotKey: jmp Old_Ss ; pass it on to old routine
System_Services endp
;
; int 74h (mouse) enters here. This also shows there is user activity.
;
Mouse proc far
call Action ; record something happenned
jmp Old_Mouse ; pass it on
Mouse endp
;
; Common processing for keyboard and mouse action.
; Reset timeout interval; turn screen back on if it was off.
;
Action proc near
push ax ; save a register
cmp Idle_Count,0 ; have we reached zero
jne Reset ; no, just reset count
; Count had reached zero, so we would have disabled the screen.
; Need to turn it back on here.
push dx
mov dx,SAR ; Get the current SDR index
in al,dx ; ... value and save it
push ax ; ...
mov al,Clocking_Mode_Index ; Then point it to clocking mode
out dx,al ; ... register.
mov dx,SDR ; Set the clocking mode register
mov al,Save_Reg ; ... back to what it was
out dx,al
mov dx,SAR ; Reset SAR to what it was
pop ax ; ...
out dx,al ; ...
pop dx
Reset: mov ax,Idle_Max ; reset count to max
mov Idle_Count,ax ; ...
pop ax ; restore
ret ; return
Action endp
;
; int 1ch (timer tick) enters here. If blanking enabled, decrement count
; and see if time to blank screen.
;
Timer proc far
cmp Disabled,True ; are we active?
je Done ; no, quit
cmp Idle_Count,0 ; already turned off?
je Done ; ok, nothing to do
dec Idle_Count ; subtract one...
jnz Done ; quit if still nonzero
; Count has reached zero. Turn off the display.
push dx
push ax
mov dx,SAR ; Get the current SDR index
in al,dx ; ... value and save it
push ax ; ...
mov al,Clocking_Mode_Index ; Then point it to clocking mode
out dx,al ; ... register.
mov dx,SDR ; Get the clocking mode register
in al,dx ; ...
mov Save_Reg,al ; Save it away for later
or al,Screen_Mask ; set the video off bit
out dx,al ; ...
mov dx,SAR ; Reset SAR to what it was
pop ax ; ...
out dx,al ; ...
pop ax
pop dx
Done: jmp Old_Timer ; continue with other int routine.
Timer endp
page
;
; int 2fh (Multiplex) enters here. This is used for communication from
; later runs of Scrnsav2 program.
;
Multiplex proc far
cmp ah,Multiplex_No ; is this our number?
je Mine ; yes,...
jmp Old_Multiplex ; no, pass it on.
Mine: cmp al,Get_Installed_State ; are we just testing?
jne Mine2 ; work to do
; Set result to indicate installed. Also, pass back our name as further
; check for someone else using the number.
mov al,0ffh ; code to say we're here
push ds ; copy ds to es
pop es ; ...
lea di,es:Scrnsav_Str ; offset for our name
iret ; return it to caller.
; Look for other function requests
Mine2: cmp al,Scrnsav_Enable
jne Mine3
mov Disabled,False ; set enabled
jmp Valid
Mine3: cmp al,Scrnsav_Disable
jne Mine4
mov Disabled,True ; set disabled
jmp Valid
Mine4: cmp al,Scrnsav_Set_Interval
jne Invalid
mov Idle_Max,bx ; reset interval
Valid: mov al,0 ; set success code
iret
Invalid: mov al,1 ; return error code
iret
Scrnsav_Str db "SCRNSAV2"
Multiplex endp
page
;
; Initialization. Print a greeting, process the parameters, determine
; whether already loaded, then either stay resident or communicate
; with resident version.
;
Initialize proc near
assume ds:Scrnsav2
push bx ; save registers we use
push cx
push si
push di
push ds
push es
push dx
push cs ; copy cs to ds.
pop ds
cld ; always want to increment
mov dx,offset Greeting ; message address
mov ah,Print_String ; function code
int Dos@ ; write the message
; Determine if this will work on the machine we're running on.
mov ax,Read_Display_Combination_Code ; function code
int Video_Io@ ; Bios call
cmp al,1ah ; is it supported?
jne NoVGA ; nope; so wrong machine
cmp bl,07h ; VGA mono?
je HaveVGA ; ...
cmp bl,08h ; VGA color?
je HaveVGA ; ...
NoVGA: mov dx,offset VGA_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
jmp Unload
;
; Process the parameters. Should be integer number of minutes,
; and/or /d (disable), /e (enable), /m <int>
;
HaveVGA:
mov si,offset Cmdline ; index of parameters
lodsb ; pick up count and step
mov ah,0 ; extend
mov cx,ax ; copy to count register
Next_Par:
call Skipblanks ; find first character
jnz Have_Par ; count reached zero
jmp End_Pars
Have_Par:
cmp byte ptr [si],'/' ; option flag?
jne Try_Int
inc si ; step to flag char
dec cx ; decrement count
jz Bad_Flag ; missing flag spec
cmp byte ptr [si],'d' ; test for disable flag
je Disable
cmp byte ptr [si],'D'
jne Try_E ; not disable
Disable: mov Par_Disable,True ; set the flag
inc si ; step over it
dec cx ; adjust count
jmp Next_Par ; and look for more pars
Try_E: cmp byte ptr [si],'e' ; test for enable flag
je Disable
cmp byte ptr [si],'E'
jne Try_M ; not enable
Enable: mov Par_Disable,False ; set the flag
inc si ; step over it
dec cx ; adjust count
jmp Next_Par ; and look for more pars
Try_M: cmp byte ptr [si],'m' ; test for m flag
je M_Flag
cmp byte ptr [si],'M'
jne Bad_Flag ; invalid flag
M_Flag: inc si ; step over it
dec cx ; adjust count
call Skipblanks ; need a following integer par
jz Bad_M_Flag ; no following par
call GetI ; find a number
jo Bad_M_Flag ; it overflows
jz Bad_M_Flag ; it wasn't there
cmp ax,80h ; check the range
jl Bad_M_Flag ; ... too small
cmp ax,0ffh ; ...
ja Bad_M_Flag ; ... too big
mov Par_Multiplex_No,al ; just right
jmp Next_Par ; look for more
; Not a flag; should be integer number of minutes.
Try_Int:
call GetI ; find a number
jo Too_Big ; it overflowed
jz Bad_Par ; not found
cmp ax,Max_Time ; is it too big?
ja Too_Big ; yes, don't allow.
mov Par_Minutes,ax ; save it away
mov Par_Set_Interval,True ; remember it was specified
jmp Next_Par ; look for more
; Invalid flag; back up to the / character before echoing it.
Bad_Flag:
dec si ; back up one pos
inc cx ; increase count
Bad_Par: mov dx,offset Bad_Par_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
Echo: lodsb ; get next character
cmp al,' ' ; stop at a blank
je End_Echo
mov dl,al ; and echo it
mov ah,Print_Char ; ...
int Dos@ ; ...
loop Echo
End_Echo:
mov dx,offset Bad_Par_Message2
mov ah,Print_String ; function code
int Dos@ ; write the end of the message
jmp Unload
Too_Big: mov dx,offset Too_Big_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
jmp Unload
Bad_M_Flag:
mov dx,offset Bad_M_Flag_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
jmp Unload
page
;
; Parameter processing finished.
;
End_Pars:
mov ax,Par_Minutes ; compute ticks required
mul Ticks ; ...
mov Par_Ticks,ax ; ...
;
; Determine whether already installed.
;
mov ah,Par_Multiplex_No ; ask if already installed...
mov al,Get_Installed_State ; ...
int Multiplex@ ; ...?
cmp al,0 ; is it installed?
je Install ; no, go do it
cmp al,0ffh ; seems to be, make sure
jne Cant_Install ; something wrong
; Last test: if it is installed, should get back es:di pointing to
; our name.
lea si,Scrnsav_Str
mov cx,size Scrnsav_Str
repnz cmpsb ; compare the bytes
jne Cant_Install
;
; Seems to be already installed, so pass across requested state.
;
mov ah,Par_Multiplex_No ; First set enabled/disabled.
mov al,Scrnsav_Enable ; assume enabling
cmp Par_Disable,True ; are we really?
jne Send_Enable ; ...
mov al,Scrnsav_Disable ; ... nope
Send_Enable:
int Multiplex@ ; Send it to resident copy.
cmp al,0 ; check for problems
jne Cant_Change
cmp Par_Set_Interval,True ; Do we want to change interval?
jne No_Change
mov ah,Par_Multiplex_No ; yes, set up parameters
mov al,Scrnsav_Set_Interval ; ...
mov bx,Par_Ticks ;...
int Multiplex@ ; Send it to resident copy.
cmp al,0 ; check for problems
jne Cant_Change
;
; Write out what we did.
;
No_Change:
mov ax,offset Null_Message ; no "installed and" part
call Write_Status ; call common routine
jmp Unload_Ok ; and terminate
;
; Problems
;
Cant_Install:
mov dx,offset Cant_Install_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
jmp Unload
Cant_Change:
mov dx,offset Cant_Change_Message
mov ah,Print_String ; function code
int Dos@ ; write the message
jmp Unload
page
;
; Appears to be OK to install, so lets do so.
;
Install:
mov al,Par_Disable ; Set enable/disable state
mov Disabled,al ; ...
mov ax,Par_Ticks ; And interval
mov Idle_Count,ax ; ...
mov Idle_Max,ax ; ...
mov al,Par_Multiplex_No ; and multiplex number
mov Multiplex_No,al ; ...
;
; Set up the interrupt vectors
;
mov ah,Get_Int
mov al,System_Services@ ; Get int 15.
int Dos@
mov Old_Ss,bx ; Save it away
mov Old_Ss+2,es
mov ah,Set_Int
mov al,System_Services@ ; Set new int 15.
mov dx,offset System_Services
int Dos@ ; ...
mov ah,Get_Int
mov al,Mouse@ ; Get int 74.
int Dos@
mov Old_Mouse,bx ; Save it away
mov Old_Mouse+2,es
mov ah,Set_Int
mov al,Mouse@ ; Set new int 74
mov dx,offset Mouse
int Dos@ ; ...
mov ah,Get_Int
mov al,Multiplex@ ; Get int 2f.
int Dos@
mov Old_Multiplex,bx ; Save it away
mov Old_Multiplex+2,es
mov ah,Set_Int
mov al,Multiplex@ ; Set new int 2f
mov dx,offset Multiplex
int Dos@ ; ...
mov ah,Get_Int
mov al,Timer@ ; Get int 1c
int Dos@
mov Old_Timer,bx ; Save it away
mov Old_Timer+2,es
mov ah,Set_Int
mov al,Timer@ ; Set new int 1c.
mov dx,offset Timer
int Dos@ ; ...
;
; Put out a message saying what we did.
;
mov Par_Set_Interval,True ; always include the interval
mov ax,offset Installed_Message
call Write_Status ; call common code
;
; Terminate and stay resident.
;
mov ah,TSR ; terminate/stay resident
mov al,0 ; ... with exit code 0
pop dx ; pop this off here... we don't restore it
mov dx,((offset Initialize - offset Scrnsav2) + 15)/16
jmp Return
; Terminate and unload (OK)
Unload_Ok:
mov al,00h ; Error code 0
jmp Unload2 ; merge
; Terminate and unload with error code.
Unload: mov al,01h ; with error code 1
Unload2: mov ah,Terminate ; terminate
pop dx ; restore dx
Return:
pop es
pop ds
pop di
pop si
pop cx ; restore registers
pop bx
int Dos@ ; back to DOS.
Initialize endp
page
;
; Write_Status writes a message saying what we did.
;
; At entry: ax = message "installed and" if required.
;
Write_Status proc near
push dx ; save reg we clobber
push ax ; save parameter
mov dx,offset Status_Message ; "Screen saver "
mov ah,Print_String
int Dos@
pop dx ; "installed and "
mov ah,Print_String
int Dos@
mov dx,offset Enabled_Message ; "enabled"
cmp Par_Disable,True
jne Ws_Enable
mov dx,offset Disabled_Message ; "disabled"
Ws_Enable:
mov ah,Print_String
int Dos@
cmp Par_Set_Interval,True
jne Ws_Done
mov dx,offset Lpar_Message ; " ("
mov ah,Print_String
int Dos@
mov ax,Par_Minutes ; <integer>
call WriteI
mov dx,offset Rpar_Message ; " minutes)"
mov ah,Print_String
int Dos@
Ws_Done:
mov dx,offset End_Message ; "."
mov ah,Print_String
int Dos@
pop dx ; restore reg
ret
Write_Status endp
page
;
; Skipblanks skips over blanks and returns at non blank.
;
; At entry: si = index of first byte to check
; cx = count of characters in string
;
; At return: si = index of non blank
; cx = remaining characters
; z bit = clear if non blank found
; = set if no nonblanks
;
; (Could do this with a rep scsb instruction, but setting it up
; is more hassle than its worth...)
;
Skipblanks proc near
Sb_Loop:
cmp cx,0 ; any charaters left?
je Sb_Ret ; nope
cmp byte ptr [si],' ' ; is it blank?
jne Sb_Ret ; nope, found something
inc si ; step to next
dec cx ; decrement count
jmp Sb_Loop ; ... and continue
Sb_Ret:
ret
Skipblanks endp
page
;
; GetI converts an ascii string to an integer. It returns
; on encountering a non-digit.
;
; At entry: si = index of first byte to convert
; cx = count of characters in string
;
; At return: si = index of first non digit
; cx = remaining characters
; ax = converted integer
; o bit is set if result overflows a single register
; z bit = clear if integer found
; = set if no integer
;
GetI proc near
push bx
push dx
push cx ; copy cx for testing at end
cmp cx,0 ; see if we have any characters
jz Gi_Ret ; ... and return if not.
mov ax,0 ; initialize result in ax
Gi_Loop: mov bl,[si] ; pick up a byte
cmp bl,'0' ; check it is in range
jb Gi_Ret ; ...
cmp bl,'9' ; ...
ja Gi_Ret ; ...
inc si ; step to next character
mul Ten ; accumulate result in ax/dx
jo Gi_RetO ; overflowed
sub bl,'0' ; convert digit to 0 ... 9
mov bh,0 ; ... word value
add ax,bx ; add to result so far
jo Gi_RetO ; overflowed
loop Gi_Loop ; decrement count and continue
jmp Gi_Ret ; merge below
Gi_RetO: pop bx ; pop off saved cx
jmp Gi_Ret2 ; merge below
Gi_Ret: pop bx ; pop back saved cx
cmp bx,cx ; and set z bit to whether we found num
Gi_Ret2: pop dx ; restore regs (z or o bits set)
pop bx ; ...
ret
GetI endp
page
;
; WriteI converts a positive, word, integer to characters and writes
; them to stdout. It uses recursion to emit the digits in the
; right order.
;
; At entry: ax = number to convert
;
WriteI proc near
push dx ; save reg we use
cmp ax,10 ; more than one digit?
jb Wi_Digit ; nope, just do the digit
mov dx,0 ; extend the dividend
div Ten ; quot-> ax, rem-> dx
call WriteI ; handle the quotient
mov ax,dx ; followed by the remainder
Wi_Digit:
add al,'0' ; convert digit to char
mov dl,al ; and write it out...
mov ah,Print_Char
int Dos@
pop dx ; restore
ret ; and return
WriteI endp
page
;
; Data area used during option parsing.
;
Par_Disable db False ; Flags for options ...
Par_Set_Interval db False ; ... specified
Par_Multiplex_No db Scrnsav_Multiplex_Number ; default number to use
Par_Minutes dw Default_Time ; interval in minutes
Par_Ticks dw ? ; interval in ticks
; Constants for mul/div instructions
Ticks dw Ticks_Per_Minute
Ten dw 10
; Message emitted
Greeting db 'Scrnsav2 version 2.0. (c) Alan Ballard 1988.',Cr,Lf,'$'
VGA_Message db Cr,Lf,'PS/2 with VGA adaptor is required.',Cr,Lf,'$'
Bad_Par_Message db Cr,Lf,'Invalid command parameter: "$'
Bad_Par_Message2 db '".',Cr,Lf
db ' Parameters are',Cr,Lf
db ' integer (number of minutes till blanking)',Cr,Lf
db ' /d (disable blanking)',Cr,Lf
db ' /e (enable blanking)',Cr,Lf
db ' /m integer (change multiplex number used)',Cr,Lf
db '$'
Too_Big_Message db Cr,Lf,'Blanking time too big. Maximum is 30.',Cr,Lf, '$'
Bad_M_Flag_Message db Cr,Lf,'Invalid /m option. Must be followed by '
db 'number between 128 and 255.',Cr,Lf,'$'
Cant_Install_Message db Cr,Lf,"Can't install SCRNSAV2. "
db 'Try a different "multiplex number" (/m option).',Cr,Lf,'$'
Cant_Change_Message db Cr,Lf,"Problems communicating with installed SCRNSAV2. "
db 'Try a different "multiplex number" (/m option).',Cr,Lf,'$'
Status_Message db 'Screen saver $'
Installed_Message db 'installed and $'
Null_Message db '$'
Enabled_Message db 'enabled$'
Disabled_Message db 'disabled$'
Lpar_Message db ' ($'
Rpar_Message db ' minutes)$'
End_Message db '.',Cr,Lf,'$'
Scrnsav2 ends
end Start